package com.atlassian.jira.plugin.report;

import java.io.IOException;
import java.util.Map;

import org.apache.log4j.Category;

import com.atlassian.jira.issue.CustomFieldManager;
import com.atlassian.jira.issue.IssueFactory;
import com.atlassian.jira.issue.search.*;
import com.atlassian.jira.issue.statistics.ExtendedStatsGroup;
import com.atlassian.jira.issue.statistics.FilterStatisticsValuesGenerator;
import com.atlassian.jira.issue.statistics.StatisticsMapper;
import com.atlassian.jira.plugin.report.impl.AbstractReport;
import com.atlassian.jira.security.JiraAuthenticationContext;
import com.atlassian.jira.util.map.EasyMap;
import com.atlassian.jira.web.action.ProjectActionSupport;
import com.atlassian.util.profiling.UtilTimerStack;
import com.opensymphony.user.User;

public final class MultipleLevelGroupByReport extends AbstractReport
{
	private static final Category log = Category.getInstance(MultipleLevelGroupByReport.class);
	private final SearchProvider searchProvider;
	private final JiraAuthenticationContext authenticationContext;
	private final SearchRequestManager searchRequestManager;
	private final IssueFactory issueFactory;
	private final CustomFieldManager customFieldManager;
	private final SearchProviderFactory searchProviderFactory;

	/**
	 * Main Constructor.
	 *
	 * @param searchProvider the <code>SearchProvider</code>
	 * @param authenticationContext the <code>JiraAuthenticationContext</code>
	 * @param searchRequestManager the <code>SearchRequestManager</code>
	 * @param customFieldManager the <code>CustomFieldManager</code>
	 * @param searchProviderFactory the <code>SearchProviderFactory</code>
	 * @param issueFactory the <code>IssueFactory</code>
	 */
	public MultipleLevelGroupByReport(SearchProvider searchProvider,
									  JiraAuthenticationContext authenticationContext,
									  SearchRequestManager searchRequestManager,
									  CustomFieldManager customFieldManager,
									  SearchProviderFactory searchProviderFactory,
									  IssueFactory issueFactory)
	{
		this.searchProvider = searchProvider;
		this.authenticationContext = authenticationContext;
		this.searchRequestManager = searchRequestManager;
		this.customFieldManager = customFieldManager;
		this.searchProviderFactory = searchProviderFactory;
		this.issueFactory = issueFactory;
	}

	/**
	 * Gets the <code>com.atlassian.jira.plugin.report.ExtendedStatsGroup</code>
	 * @param sr the <code>com.atlassian.jira.issue.search.SearchRequest</code>
	 * @param user the required<code>com.opensymphony.user.User</code>
	 * @param mapper1 the <code>com.atlassian.jira.issue.statistics.StatisticsMapper</code> of the first grouping criterion
	 * @param mapper2 the <code>com.atlassian.jira.issue.statistics.StatisticsMapper</code> of the second grouping criterion
	 * @return an <code>com.atlassian.jira.plugin.report.ExtendedStatsGroup</code>
	 */
	private ExtendedStatsGroup getExtendedStatsGroup(SearchRequest sr,
													 User user,
													 StatisticsMapper mapper1,
													 StatisticsMapper mapper2)
	{

		try
		{
			return searchMapIssueKeys(sr, user, mapper1, mapper2);
		}
		catch( SearchException e )
		{
			log.error("Exception rendering " + this.getClass().getName() + ".  Exception " + e.getMessage(), e);
			return null;
		}
		finally
		{
			UtilTimerStack.pop("Returned Options");
		}
	}

	/**
	 * Creates an <code>com.atlassian.jira.plugin.report.ExtendedStatsGroup</code> using mapper1 and mapper2
	 * The <code>ExtendedStatsGroup</code> will be used for creating a
	 * <code>com.atlassian.jira.plugin.report.TwoDimensionalDocIssueHitCollector</code>
	 * Uses the <code>TwoDimensionalDocIssueHitCollector</code> to search and sort with a <code>SearchProvider</code>
	 * @param request
	 * @param user
	 * @param mapper1
	 * @param mapper2
	 * @return an <code>com.atlassian.jira.plugin.report.ExtendedStatsGroup</code>
	 * that has been created using the mapper1 and mapper2
	 * @throws SearchException
	 */
	private ExtendedStatsGroup searchMapIssueKeys(SearchRequest request,
												 User user,
												 StatisticsMapper mapper1,
												 StatisticsMapper mapper2) throws SearchException
	{
		try
		{
			ExtendedStatsGroup extendedStatsGroup = new ExtendedStatsGroup(mapper1, mapper2);
			TwoDimensionalDocIssueHitCollector hitCollector =
					new TwoDimensionalDocIssueHitCollector
							(
									extendedStatsGroup,
									mapper1.getDocumentConstant(),
									mapper2.getDocumentConstant(),
									searchProviderFactory.getSearcher(SearchProviderFactory.ISSUE_INDEX),
									issueFactory
							);

			searchProvider.searchAndSort(request, user, hitCollector);
			return extendedStatsGroup;
		}
		catch( IOException e )
		{
			throw new SearchException(e);
		}
		finally
		{
			UtilTimerStack.pop("Search Count Map");
		}
	}

	public String generateReportHtml(ProjectActionSupport action, Map params) throws Exception
	{
		String filterId = (String) params.get("filterid");
		if( filterId == null )
		{
			log.error("Multiple Level Group By Report run without a selected project: params=" + params);
			return ("<span class='errMsg'>No search filter has been selected. Please <a href=\"IssueNavigator.jspa?reset=Update&amp;pid="
					+ params.get("selectedProjectId")
					+ "\">create one</a>, and re-run this report. See also <a href=\"http://jira.atlassian.com/browse/JRA-5042\">JRA-5042</a></span>");
		}

		String mapperName1 = (String) params.get("mapper1");
		String mapperName2 = (String) params.get("mapper2");
		final StatisticsMapper mapper1 = new FilterStatisticsValuesGenerator().getStatsMapper(mapperName1);
		final StatisticsMapper mapper2 = new FilterStatisticsValuesGenerator().getStatsMapper(mapperName2);
		final SearchRequest request = searchRequestManager.getRequest(authenticationContext.getUser(),
																	  new Long(filterId));

		final Map startingParams;
		try
		{
			ExtendedStatsGroup extendedStatsGroup = getExtendedStatsGroup(request, authenticationContext.getUser(), mapper1,
																		  mapper2);

			startingParams = EasyMap.build("action", action,
										   "statsGroup", extendedStatsGroup,
										   "searchRequest", request,
										   "mapperType1", mapperName1,
										   "customFieldManager", customFieldManager,
										   "portlet", this);
			startingParams.put("mapperType2", mapperName2);

			return descriptor.getHtml("view", startingParams);
		}
		finally
		{
			UtilTimerStack.pop("Generate Report HTML");
		}
	}

}